package net.borderwars.convert;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.StringTokenizer;

/**
 * @author Eric
 *         Date: Mar 5, 2005
 *         Time: 4:30:28 PM
 */
public class CRobotMethod {
    BufferedReader br = null;
    String name = null;
    private LinkedList<String> args;

    public CRobotMethod (BufferedReader br, String name) throws IOException {
        this.br = br;
        this.name = name.trim ();
        // System.err.println("Starting function " + name);
        advanceUntil ("Local symbol table:");
        readTableUntil (localSymbolTable, "External symbol table");
        readTableUntil (externalSymbolTable, "Function symbol table");
        readTableUntil (functionSymbolTable, "Generated");
        readGeneratedCode (generatedCode);
    }

    public void addMethod (ClassGen cg, String origFile, String className) throws IOException {
        Method m = null;
        /**
         * MethodGen(int access_flags, Type return_type, Type[] arg_types, java.lang.String[] arg_names, java.lang.String method_name, java.lang.String class_name, InstructionList il, ConstantPoolGen cp)
         */

        int accessFlags = Constants.ACC_PUBLIC;
        Type returnType = Type.INT; // or

        Type args[] = new Type[this.args.size ()];
        String argNames[] = new String[this.args.size ()];
        int i = 0;
        for (String argName : this.args) {
            argNames[i] = argName;
            args[i] = Type.INT;
            i++;
        }
        CRobotOpCode.resetState ();
        String methodName = name;
        InstructionList il = new InstructionList ();
        ConstantPoolGen cpg = cg.getConstantPool ();
        mg = new MethodGen (accessFlags, returnType, args, argNames, methodName, className, il, cpg);
        ConstantPool cp = cpg.getConstantPool ();

        buildInstructions (il, cg, mg);

//        mg.setMaxStack(localVars.length);
//
//       // LocalVariableTable lvt = mg.getLocalVariableTable(cpg);
//
//        lvt.setLocalVariableTable(localVars);

        cg.addMethod (mg.getMethod ());

    }

    public MethodGen mg = null;

    private void buildInstructions (InstructionList il, ClassGen cg, MethodGen mg) {
        this.mg = mg;
        for (String line : generatedCode) {
            if (line.indexOf (":") == -1) // if its a blank line
                continue;
            il.append (CRobotOpCode.getInstructions (line, cg, this, mg));
        }
    }

    public String toString () {
        return (name + " Local: " + localSymbolTable.size () + " External " + externalSymbolTable.size () + " Function " + functionSymbolTable.size () + " instruction " + generatedCode.size ());
    }

    private void readGeneratedCode (ArrayList<String> generatedCode) throws IOException {
        String line = null;
        do {
            line = br.readLine ();
            generatedCode.add (line);
        }
        while (line.indexOf (":") != -1);
    }

    private void readTableUntil (ArrayList<String> table, String until) throws IOException {
        String line = null;
        do {
            line = br.readLine ();
            StringTokenizer st = new StringTokenizer (line, ": ");
            if (line.indexOf (until) == -1) {
                while (st.hasMoreTokens ()) {
                    int position = Integer.parseInt (st.nextToken (), 10);
                    String symbol = st.nextToken ();
                    table.add (position, symbol);
                }
            }
        }
        while (line.indexOf (until) == -1);
    }

    private String advanceUntil (String lookFor) throws IOException {
        String line = null;
        do {
            line = br.readLine ();
        }
        while (line.indexOf (lookFor) == -1);
        return (line);
    }

    static ArrayList<String> localSymbolTable = new ArrayList<String> ();
    static ArrayList<String> externalSymbolTable = new ArrayList<String> ();
    static ArrayList<String> functionSymbolTable = new ArrayList<String> ();
    static ArrayList<String> generatedCode = new ArrayList<String> ();

    public void calcArgCount (ArrayList<String> linesBeforeFunction) {
        String methodName = (String) linesBeforeFunction.get (linesBeforeFunction.size () - 1);
        methodName = methodName.substring (methodName.indexOf (":") + 1).trim ();
        //System.out.println("method " + methodName);

        for (String line : linesBeforeFunction) {
            if (line.indexOf (methodName) != -1 && line.indexOf ("(") != -1 && line.indexOf (")") != -1) { // if this is the declartion line
                LinkedList<String> args = new LinkedList<String> ();
                int a = line.indexOf ("(");
                int b = line.indexOf (")");
                //  System.out.println(line + " " + methodName+ " " + a + " " + b);
                if (b - a == 1) {
                    setArgs (args);
                    return;
                }
                else {
                    String argSpots = line.substring (a, b);
                    StringTokenizer st = new StringTokenizer (argSpots, ",()");
                    while (st.hasMoreTokens ()) {
                        args.add (st.nextToken ());
                    }
//                    int commaCount =0;
//                    while(line.charAt(a)  != ')') {
//                        if(line.charAt(a)== ',') {
//                            commaCount++;
//                        }
//                        a++;
//                    }
                    setArgs (args);
                    return;
                }
            }
        }

    }

    private void setArgs (LinkedList<String> args) {
        //System.out.println(name + " Setting argcount to " + args);
        this.args = args;
    }
}
